/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-06-19     hehung       the first version
 */
#include "app_pluse_sensor.h"
#include <rtdevice.h>
#include <rtthread.h>
#include "hal_data.h"
#include "app_common.h"
#include "r_agt.h"
#include "r_timer_api.h"

/****************************************************************
  * 术语：Pluse Sensor - PS - 心率传感器
  * 功能：该模块为心率监测模块的驱动代码，使用ADC采集心率信息并通过
  * 算法解析出来当前的心率数据，定时器实现周期性采集ADC
  * 使用引脚：P000 - ADC0 CH0
 ***************************************************************/

/* Pluse Sensor - PS - 心率传感器 */
/* 使用引脚： P000 - ADC0 CH0 - 采样精度为10bits */

#define PS_ADC_DEBUG                        (0U)

#define PS_ADC_DEV_NAME                     ("adc0")      /* ADC 设备名称 */
#define PS_ADC_DEV_CHANNEL                  (0U)           /* ADC 通道 */
#define PS_REFER_VOLTAGE                    (330U)         /* 参考电压 3.3V,数据精度乘以100保留2位小数*/
#define PS_CONVERT_BITS                     (1U << 10U)    /* 转换位数为12位 */

#define THREAD_STACK_SIZE_PS                (1024*5U)
#define THREAD_PRIORITY_PS                  (1U)

#define PS_DEBUG                            (COMMON_ON)
#define PS_PRINT_EN                         (COMMON_OFF)
#if (PS_PRINT_EN == COMMON_ON)
#define ps_printf(fmt, ...)                 rt_kprintf(fmt, ##__VA_ARGS__)
#else
#define ps_printf(fmt, ...)
#endif

static rt_thread_t thread_ps_hd = RT_NULL;

/* ADC设备 */
rt_adc_device_t adc_dev;

//心率采集相关变量
int BPM;                            //脉搏率==就是心率
int Signal;                         //传入的原始数据。
int IBI = 600;                      //节拍间隔，两次节拍之间的时间（ms）。计算：60/IBI(s)=心率（BPM）
bool Pulse = false;        //脉冲高低标志。当脉波高时为真，低时为假。
bool QS = false;           //当发现一个节拍时，就变成了真实。
int rate[10];                       //数组保存最后10个IBI值。
uint32_t sampleCounter = 0;    //用于确定脉冲定时。
uint32_t lastBeatTime = 0;     //用于查找IBI
int P =512;                         //用于在脉冲波中寻找峰值
int T = 512;                        //用于在脉冲波中寻找波谷
int thresh = 512;                   //用来寻找瞬间的心跳
int amp = 100;                      //用于保持脉冲波形的振幅
int Num;
uint8_t firstBeat = true;     //第一个脉冲节拍
uint8_t secondBeat = false;   //第二个脉冲节拍，这两个变量用于确定两个节拍

/* ADC采样值保留 128个用于OLED显示 */
uint16_t ps_waveformlist[128] = {0};

static void Ps_Thread_Entry(void *para);
static void Ps_HeartRateDeal(void);
static void Ps_ReadSampleValueFromAdc(void);
static void Ps_WaveformDeal(uint16_t adc_value);

void Ps_TaskCreate(void)
{
    thread_ps_hd = rt_thread_create("thread_ps",
                                    Ps_Thread_Entry,
                                    RT_NULL,
                                    THREAD_STACK_SIZE_PS,
                                    THREAD_PRIORITY_PS,
                                    10);
    if (RT_NULL != thread_ps_hd)
    {
        rt_thread_startup(thread_ps_hd);
    }
}

void Ps_TaskDelete(void)
{
    rt_thread_delete(thread_ps_hd);
}

/* Pluse sensor任务入口 */
static void Ps_Thread_Entry(void *para)
{
#if (PS_DEBUG == COMMON_ON)
    uint32_t cnt = 0;
#endif

    /* 查找设备 */
    adc_dev = (rt_adc_device_t)rt_device_find(PS_ADC_DEV_NAME);
    if (adc_dev == RT_NULL)
    {
        ps_printf("adc driver run failed! can't find %s device!\n", PS_ADC_DEV_NAME);
    }

    /* 使能设备 */
   (void)rt_adc_enable(adc_dev, PS_ADC_DEV_CHANNEL);

    /* 初始化定时器，使用定时器来判断按键按下的时间 */
    (void) R_AGT_Open(&g_timer1_ctrl, &g_timer1_cfg);
    (void) R_AGT_Reset(&g_timer1_ctrl);
    (void) R_AGT_Start(&g_timer1_ctrl);
//    (void) R_AGT_Stop(&g_timer1_ctrl);
    while (1)
    {
//        Ps_HeartRateDeal();
//        Ps_ReadSampleValueFromAdc();

#if (PS_DEBUG == COMMON_ON)
        if (cnt == 250)
        {
            cnt = 0;
            ps_printf("\nIBI = %d, BPM = %d, Adc = %d", IBI, BPM, Signal);
        }
        else
        {
           cnt ++;
        }
#endif

        rt_thread_mdelay(2);
    }
}

/* 阅读采集自心率传感器的ADC值 */
static void Ps_ReadSampleValueFromAdc(void)
{
    rt_uint32_t value;
    /* 读取采样值 */
    value = rt_adc_read(adc_dev, PS_ADC_DEV_CHANNEL);

    /* 转换采集到的ADC值的精度为10bits */
    Signal = value;
    Ps_WaveformDeal(value);
}

/* 采集心率信号的处理函数 */
static void Ps_HeartRateDeal(void)
{
    unsigned int runningTotal;
    uint8_t i;

    sampleCounter += 2;
    Num = sampleCounter - lastBeatTime;             //监控最后一次节拍后的时间，以避免噪声

    //找到脉冲波的波峰和波谷
    if((Signal < thresh) && (Num > (IBI/5)*3))  //为了避免需要等待3/5个IBI的时间
    {
        if(Signal < T)
        {                                       //T是阈值
            T = Signal;                         //跟踪脉搏波的最低点，改变阈值
        }
    }
    if((Signal > thresh) && (Signal > P))       //采样值大于阈值并且采样值大于峰值
    {
        P = Signal;                             //P是峰值，改变峰值
    }
    //现在开始寻找心跳节拍
    if (Num > 250)              //避免高频噪声
    {
        if ((Signal > thresh) && (Pulse == false) && (Num > (IBI/5)*3))
        {
            Pulse = true;                               //当有脉冲的时候就设置脉冲信号。
//              LED_ON();                                   //打开LED，表示已经有脉冲了
            IBI = sampleCounter - lastBeatTime;         //测量节拍的ms级的时间
            lastBeatTime = sampleCounter;               //记录下一个脉冲的时间。
            if(secondBeat)          //如果这是第二个节拍，如果secondBeat == TRUE，表示是第二个节拍
            {
                secondBeat = false;                  //清除secondBeat节拍标志
                for(i=0; i<=9; i++)     //在启动时，种子的运行总数得到一个实现的BPM。
                {
                    rate[i] = IBI;
                }
            }
            if(firstBeat)           //如果这是第一次发现节拍，如果firstBeat == TRUE。
            {
                firstBeat = false;                   //清除firstBeat标志
                secondBeat = true;                   //设置secongBeat标志
                return;                              //IBI值是不可靠的，所以放弃它。
            }
            //保留最后10个IBI值的运行总数。
            runningTotal = 0;                  //清除runningTotal变量

            for(i=0; i<=8; i++)             //转换数据到rate数组中
            {
                rate[i] = rate[i+1];                  //去掉旧的的IBI值。
                runningTotal += rate[i];              //添加9个以前的老的IBI值。
            }

            rate[9] = IBI;                          //将最新的IBI添加到速率数组中。
            runningTotal += rate[9];                //添加最新的IBI到runningTotal。
            runningTotal /= 10;                     //平均最后10个IBI值。
            BPM = 60000/runningTotal;               //一分钟有多少拍。即心率BPM
            if(BPM>200)
                BPM=200;         //限制BPM最高显示值
            if(BPM<30)
                BPM=30;               //限制BPM最低显示值
            QS = true;
        }
    }

    if (Signal < thresh && Pulse == true)       //当值下降时，节拍就结束了。
    {
//          LED_OFF();                              //关闭LED
        Pulse = false;                         //重设脉冲标记，这样方便下一次的计数
        amp = P - T;                           //得到脉冲波的振幅。
        thresh = amp/2 + T;                    //设置thresh为振幅的50%。
        P = thresh;                            //重新设置下一个时间
        T = thresh;
    }

    if (Num > 2500)             //如果2.5秒过去了还没有节拍
    {
        thresh = 512;                          //设置默认阈值
        P = 512;                               //设置默认P值
        T = 512;                               //设置默认T值
        lastBeatTime = sampleCounter;          //把最后的节拍跟上来。
        firstBeat = true;                      //设置firstBeat为true方便下一次处理
        secondBeat = false;
    }
}

//波形处理函数，放在定时器中执行，2ms执行一次
static void Ps_WaveformDeal(uint16_t adc_value)
{
    uint16_t temp;
    static uint8_t waveSample_times = 0;
    uint8_t i = 0;

    /* 每20ms更新一次数据 */
    if(waveSample_times == 10)
    {
        waveSample_times = 0;

        /* 将ADC采样值变为0-64范围的值，便于OLED显示 */
//        temp = (uint8_t)(adc_value/16);
        temp = adc_value - 250;
        temp = 700 - temp;
        if (temp < 0)
            temp = 0;
        else if (temp > 700)
            temp = 700;
        temp = (uint8_t)(temp/11);

        /* 超过OLED显示范围之后从新开始 */
        for(i = 0; i < 128; i++)
        {
            ps_waveformlist[i] = ps_waveformlist[i+1];
        }
        ps_waveformlist[127] = temp;
    }
    else
    {
        waveSample_times ++;
    }
}

/* 返回心率值 */
int Ps_GetBpm(void)
{
    return BPM;
}

bool Ps_GetQsFlg(void)
{
    return QS;
}

void Ps_ClrQsFlg(void)
{
    QS = 0;
}

/* 获取ADC采样值 */
uint16_t Ps_GetRawAdcValue(void)
{
    return (uint16_t)Signal;
}

/* 返回128个adc采样值 */
uint16_t* Ps_GetWaveformList(void)
{
    return (&ps_waveformlist[0]);
}

/* 定时器1回调函数, 定时时间为1ms */
void Timer1_CallbackFunc(timer_callback_args_t * p_args)
{
    static bool timer_2ms_ok = false;

    if (TIMER_EVENT_CYCLE_END == p_args->event)
    {
        if (true == timer_2ms_ok)
        {
            /* Sample adc every 2ms */
            Ps_ReadSampleValueFromAdc();
            /* Calculate heart rate */
            Ps_HeartRateDeal();
        }

        timer_2ms_ok = (timer_2ms_ok == false) ? (true) : (false);
    }
}

#if (PS_ADC_DEBUG == COMMON_ON)
static int adc_vol_sample(int argc, char *argv[])
{
    rt_adc_device_t adc_dev;
    rt_uint32_t value, vol;
    rt_err_t ret = RT_EOK;

    /* 查找设备 */
    adc_dev = (rt_adc_device_t)rt_device_find(PS_ADC_DEV_NAME);
    if (adc_dev == RT_NULL)
    {
        ps_printf("adc sample run failed! can't find %s device!\n", PS_ADC_DEV_NAME);
        return RT_ERROR;
    }

    /* 使能设备 */
    ret = rt_adc_enable(adc_dev, PS_ADC_DEV_CHANNEL);

    /* 读取采样值 */
    value = rt_adc_read(adc_dev, PS_ADC_DEV_CHANNEL);
    ps_printf("the value is :%d \n", value);

    /* 转换为对应电压值 */
    vol = value * PS_REFER_VOLTAGE / PS_CONVERT_BITS;
    ps_printf("the voltage is :%d.%02d \n", vol / 100, vol % 100);

    /* 关闭通道 */
    ret = rt_adc_disable(adc_dev, PS_ADC_DEV_CHANNEL);

    return ret;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(adc_vol_sample, adc voltage convert sample);
#endif

